using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace UmassGrader
{
    /// <summary>
    /// holds information about a Lab or homework assignment
    /// </summary>
    [Serializable]
    public class Assignment
    {
        /// <summary>
        /// delegate to take a string and convert into an object of the appropriate type
        /// </summary>
        /// <param name="a">a string representation of an object</param>
        /// <returns>the object represented by the string</returns>
        public delegate object itemParser(string a);

        /// <summary>
        /// the identifier for an assignment
        /// </summary>
        public String dispName;

        /// <summary>
        /// a string containing special flags needed for compilation
        /// </summary>
        public String compilerFlags;
        /// <summary>
        /// if true try to compile the source files when grading else just display them
        /// </summary>
        public bool needsCompile;
        /// <summary>
        /// list of files that should be submited by a student
        /// </summary>
        public List<String> submitList;
        /// <summary>
        /// list of 5 dates corisponding to the due dates for labs that meet on a weekday(mon-&gt;fri)
        /// </summary>
        public List<DateTime> dueDateList;
        /// <summary>
        /// list of files that will be redirected as input to the students compiled executable. the students program will be run once for each item
        /// </summary>
        public List<String> inputRedirectList;
        /// <summary>
        /// list of files that should be created by running the project executable
        /// </summary>
        public List<String> outputFileList;
        /// <summary>
        /// these files will be copied into the working directory before each program execution. files will typically be data files or the like
        /// </summary>
        public List<String> inputFileList;
        /// <summary>
        /// Should be refactored out and local dir should only be kept inside pathfinder
        /// </summary>
        static public String localDir; //shared
        /// <summary>
        /// NOT USED ANYMORE
        /// </summary>
        static public String remoteDir; //shared

        /// <summary>
        /// constructor give fake values
        /// </summary>
        /// <remarks>gives default values</remarks>
        public Assignment()
        {
            dispName = "test";
            compilerFlags = "";
            needsCompile = true;
            submitList = new List<String>();

            dueDateList = new List<DateTime>(5);
            dueDateList.Add(DateTime.Today);
            dueDateList.Add(DateTime.Today);
            dueDateList.Add(DateTime.Today);
            dueDateList.Add(DateTime.Today);
            dueDateList.Add(DateTime.Today);

            inputRedirectList = new List<String>();
            outputFileList = new List<String>();
            inputFileList = new List<String>();
        }
        /// <summary>
        /// constructor from string
        /// </summary>
        /// <param name="myStr">string containing comma seperated data (see class remarks for format)</param>
        public Assignment(String myStr)
            : this(myStr.Split(','))
        { }
        /// <summary>
        /// constructor from array
        /// </summary>
        /// <param name="mylst">array of data see class remarks for format</param>
        public Assignment(string[] mylst)
        {
            //int count;
            int Base;

            //initialize our lists
            submitList = new List<String>();
            dueDateList = new List<DateTime>();
            inputRedirectList = new List<String>();
            outputFileList = new List<String>();
            inputFileList = new List<String>();

            //parse line
            dispName = mylst[0];
            compilerFlags = mylst[1];
            needsCompile = Boolean.Parse(mylst[2]);
            //get the number of input files then add them to the list
            itemParser strDel = delegate(string a) { return a; };
            itemParser dateDel = delegate(string a) { return DateTime.Parse(a); };
            Base = 3;
            getList(ref Base, mylst, ref submitList, strDel);
            getList(ref Base, mylst, ref dueDateList, dateDel);
            getList(ref Base, mylst, ref inputRedirectList, strDel);
            getList(ref Base, mylst, ref inputFileList, strDel);
            getList(ref Base, mylst, ref outputFileList, strDel);
        }


        /// <summary>
        /// parses an array of strings for a set number of objects of any type
        /// </summary>
        /// <typeparam name="E">the type of object to be collected</typeparam>
        /// <param name="basenum">the index in the array of strings where the number of objects to follow is stored</param>
        /// <param name="srcarray">the list of strings</param>
        /// <param name="dst">the generic list to put the objects into</param>
        /// <param name="myParse">the delegate that converst the string to the object </param>
        private void getList<E>(ref int basenum, string[] srcarray, ref List<E> dst, itemParser myParse)
        {
            int count = int.Parse(srcarray[basenum]);
            for (int i = basenum + 1; i < basenum + count + 1; i++)
                //for i As Integer = base + 1 To base + count
                dst.Add((E)myParse(srcarray[i]));
            basenum += count + 1;

        }

        /// <summary>
        /// constructer from file
        /// </summary>
        /// <param name="infile">an open stream reader</param>
        /// <remarks></remarks>
        public Assignment(StreamReader infile)
            : this(infile.ReadLine())
        { }

        /// <summary>
        /// Gives display Name
        /// </summary>
        public override string ToString()
        {
            return this.dispName;
        }
        /// <summary>
        /// Serializes the Assignment Object
        /// </summary>
        public string Serialize()
        {
            int i;
            String myString = "";
            //1st tab
            myString += string.Format("{0},{1},{2},", dispName, compilerFlags, needsCompile.ToString());
            myString += submitList.Count + ",";
            for (i = 0; i < submitList.Count; i++) myString += submitList[i] + ",";
            //2nd tab
            myString += dueDateList.Count + ",";
            for (i = 0; i < dueDateList.Count; i++) myString += dueDateList[i].ToString() + ",";
            //3rd tab
            myString += inputRedirectList.Count + ",";
            for (i = 0; i < inputRedirectList.Count; i++) myString += inputRedirectList[i] + ",";

            myString += inputFileList.Count + ",";
            for (i = 0; i < inputFileList.Count; i++) myString += inputFileList[i] + ",";

            myString += outputFileList.Count + ",";
            for (i = 0; i < outputFileList.Count; i++) myString += outputFileList[i] + ",";

            return myString;
        }
        /// <summary>
        /// Returns the Directory where all submitted files are downloaded to for a particular assignment
        /// </summary>
        public String DestinationDir
        {
            get { return String.Format(@"{0}\{1}", localDir, dispName); }
        }
        /// <summary>
        /// Returns the input data File path. whith multiple paths, provide an index
        /// </summary>
        public string inputFilePath(int num)
        {
            return String.Format(@"{0}\{1}\{2}", localDir.ToString(), dispName, inputRedirectList[num]);
        }
        /// <summary>
        /// Returns the path for the expected output files
        /// </summary>
        public string outputFilePath(int num)
        {
            // TODO(ben viall): this is wrong. find where it is used and provide a FULL path
            // TODO(ben viall): with the change to c# optional argument are no longer an option
            // this may reap havoc later...                            
            return this.outputFileList[num];
        }
        /// <summary>
        /// The Working Directory where programs will be executed
        /// </summary>
        public String workingDir
        {
            get { return String.Format(@"{0}\{1}\{2}", localDir, dispName, "workingDir"); }
        }

        /// <summary>
        /// get the list of files with compileable code in them
        /// </summary>
        public List<String> compilableFiles()
        {
            List<String> myList = new List<string>(submitList.Count);
            foreach (String path in submitList)
            {
                if (path.EndsWith(".cpp") || path.EndsWith(".c"))
                    myList.Add(path);
            }
            return myList;
        }
    }
}
